#ifndef _L1_instruction_cache_h_
#define _L1_instruction_cache_h_

#include <iostream>
#include "Set.h"
#include "L2_cache.h"

#define i_nWays      2

using namespace std;

struct LRU_2_way {
    bool bit;     
    friend ostream& operator<<(ostream& lhs, const LRU_2_way& rhs)
    {
        lhs << rhs.bit; 
        return lhs;
    }
    LRU_2_way(){
        bit = 0;       
    }
};

int LRU_way(LRU_2_way);

class L2_cache;    // forward declaration

class L1_instruction_cache {
 
private:

    Set cache[nSets][i_nWays];
    LRU_2_way LRU[nSets];


public:

/** Ports and Connections **/
    
    L2_cache* l2;                   // Connection to the L2_cache
    char *filepath;                 // path to the output file
    long reads, writes;             // Read and Write counters (output)
    long misses, hits;              // Misses and Hits counter (output)
    long invalidates, print_req;    // Invalidates and Print counter (output)

/** Methods **/    
    
    L1_instruction_cache(){
        reads = writes = misses = hits = invalidates = print_req = 0; 
        filepath = (char*) malloc(256);
        sprintf(filepath, "prints_inst.txt");
        FILE *fid = fopen(filepath,"w");
        fclose(fid);
    }
    
    unsigned int read(unsigned int addr);
    int searchWays(unsigned int addr);
    unsigned int findReplacementWay(unsigned int addr);
    void ResetState();
    void PrintContents();
    
/** Address Parser **/
    static unsigned int getOffSet(unsigned int addr) {        
        return addr & OffSetMask;          
    }        
    
    static unsigned int getIndex(unsigned int addr) {        
        return (addr >> nOffSetBits) & IndexMask;          
    }

    static unsigned int getTag(unsigned int addr) {        
        return (addr >> nOffSetBits >> nIndexBits) & TagMask;          
    }
    
};

#endif
